
package SSF.Net;

import SSF.OS.*;
import com.renesys.raceway.SSF.*;
import com.renesys.raceway.DML.*;

/** A simple FIFO packet queue. */
public class droptailQueue implements packetQueue {
  private long lastTransmitTime = 0;
  private long queueingDelay = 0;  // in ticks
  private long maxQueueingDelay = Long.MAX_VALUE;
  private NIC forNIC;
  private Host localHost;
  public boolean pktDropped;

  /** auxiliary monitoring variable */
  public int pktCount = 0;
  /** auxiliary monitoring variable */
  public int pktDropCount = 0;

  private PacketQueueMonitor myMonitor = null;

  public droptailQueue() {}

  /** Called by NIC if DML attribute <TT>interface.queue</TT> is omitted;
   *  then droptailQueue is the default packetQueue installed.
   */
  public droptailQueue(NIC nic) {
    forNIC = nic;
    maxQueueingDelay =
      (long)((new Long(nic.buffersize)).longValue()*8./nic.bitrate());
  }

  /** Does not accept any user-defined Configuration, required by interface packetQueue.
   *  Obtains values of attributes <TT>buffer</TT> and <TT>bitrate</TT>
   *  from the instance of SSF.Net.NIC that constructs this droptailQueue.
   */
  public void config(SSF.Net.NIC nic, Configuration cfg) {
    forNIC = nic;
    maxQueueingDelay =
      (long)((new Long(nic.buffersize)).longValue()*8./nic.bitrate());
  }

  /** Required by interface packetQueue. Called by NIC.init().
   */
  public void init() {
    localHost = (Host)forNIC.inGraph();
  }

  /** Set reference to a correctly configured class implementing
   *  the Java interface PacketQueueMonitor.
   */
  public void setMonitor(PacketQueueMonitor mon) {
    myMonitor = mon;
  }

  /** 
   * Logical model: The Buffer holds N bits max; bits go out one
   * by one at the NIC bitrate. If attempt to enqueue a message
   * whose size is larger than the currently  available free buffer space,
   * drop it.
   * queueingDelay is measured in integer multiples of simulated time "ticks".
   * Warning: if Net.frequency (ticks per second) is smaller than
   * NIC's bitrate (bits/sec) the delay calculations are not
   * accurate enough to obtain correct value of number of bits
   * in the buffer.
   * Transmit a message.  Each interface tracks its "queueing delay,"
   * which accumulates as packets are written out.  Each packet 
   * contributes its transmission time (size in bits / bitrate) to the 
   * queueing delay of future packets.  This guarantees that the 
   * interface will never exceed the local configured bitrate.<p> 
   * Fairness among competing flows is an entirely separate 
   * issue; without buffering and write-behind interleaving, latecomers 
   * will be delayed by early arrivals, even if they were written 
   * "simultaneously" by different traffic sources and should therefore 
   * compete for timeslots.<p>
   * If the buffer is full (that is, the number of bytes that have 
   * already been transmitted in this timeslot would fill the configured
   * buffer size),  then drop the packet rather than scheduling it. <p>
   * Return true if a packet is successfully enqueued, or false if the 
   * queue rejects the packet.
   */
  public boolean enqueue(ProtocolMessage msg) {
    pktDropped = false;

    pktCount++;
    calibrate();
    long testqueueingDelay = 
              queueingDelay + (long)(8.*msg.size() / forNIC.bitrate());
   
    if (testqueueingDelay>maxQueueingDelay) {
      forNIC.drop(msg);
      pktDropCount++;
      pktDropped = true;
    }

    if (myMonitor != null)
      myMonitor.receive(msg);

    if (pktDropped) {
      return false;
    }
    
    queueingDelay = testqueueingDelay;
    
    PacketEvent pevt = new PacketEvent(msg);
    
    if (forNIC.dumpTo!=null) forNIC.dumpTo.dump(pevt);
    
    forNIC.link_hw.transmit(forNIC,pevt,queueingDelay);

    return true;
  }
  
  /** If we have moved forward in time, recalibrate the 
   * accumulated delay so that it remains relative to "now".
   * Re-calibrate before every transmission.  Returns the 
   * recalibrated accumulated queueing delay.
   */
  private long calibrate() { 
    //long now = forNIC.inGraph().now();
    long now = localHost.now();
    if (now!=lastTransmitTime) {
      queueingDelay -= (now-lastTransmitTime);
      if (queueingDelay<0) queueingDelay = 0;
      lastTransmitTime = now;
    }
    return queueingDelay;
  }

  /** Returns queue size in bits at the moment of calling.
   *  Queue size is calculated as queueing delay
   *  (long, in tick units) times interface bitrate (double, in bits/tick)
   *  therefore it is rounded down to nearest long integer value.
   */
  public long getNumberOfBitsInQueue() {
    return (long)(forNIC.bitrate()*calibrate());
  }
}

/*=                                                                      =*/
/*=  Copyright (c) 1997--2000  SSF Research Network                      =*/
/*=                                                                      =*/
/*=  SSFNet is open source software, distributed under the GNU General   =*/
/*=  Public License.  See the file COPYING in the 'doc' subdirectory of  =*/
/*=  the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html    =*/
/*=                                                                      =*/
